package user

import (
	"bytes"
	"context"
	"errors"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/common/constants"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/dto/base"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/repository/base/user/new_user"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/utils"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/utils/ossfile"
	basePb "git.mycaigou.com/gfyx/micro-gfyx-api/proto/micro_gfyx_oppty_service_proto/micro-gfyx-oppty-service/base"
	"git.myscrm.cn/golang/stark/v4"
	"github.com/xuri/excelize/v2"
	"net/http"
	"strconv"
	"strings"
)

type userNewService struct {
	userNewRepo new_user.UserRepositoryIface
}

// NewUserNewService 构造函数
func NewUserNewService(userNewRepo new_user.UserRepositoryIface) UserNewServiceIface {
	return &userNewService{
		userNewRepo: userNewRepo,
	}
}

// UserList 用户列表
func (s *userNewService) UserList(ctx context.Context, param *base.UserListParam) ([]*base.User, *base.Page, error) {
	return s.userNewRepo.UserList(ctx, param)
}

// UserSave 新增/更新用户
func (s *userNewService) UserSave(ctx context.Context, param *base.User) (int64, error) {
	return s.userNewRepo.UserSave(ctx, param)
}

// UserDelete 删除用户
func (s *userNewService) UserDelete(ctx context.Context, param *base.UserDeleteParam) (int64, error) {
	return s.userNewRepo.UserDelete(ctx, param)
}

// UserForbid 禁止用户
func (s *userNewService) UserForbid(ctx context.Context, param *base.UserForbidParam) error {
	return s.userNewRepo.UserForbid(ctx, param)
}

// UserAuthorize 账号授权
func (s *userNewService) UserAuthorize(ctx context.Context, param *base.UserAuthorizeParam) error {
	return s.userNewRepo.UserAuthorize(ctx, param)
}

// UserImport 用户导入
func (s *userNewService) UserImport(ctx context.Context, param *base.UserImportParam) (*base.UserImportResponse, error) {
	fileUrl, err := ossfile.GetSignURL(param.FilePath)
	if err != nil {
		stark.Logger.Errorf(ctx, "UserImport-ossfile.GetSignURL fail, err:%+v", err)
		return nil, err
	}
	stark.Logger.Infof(ctx, "UserImport oss file:%s", fileUrl)
	rowDatas, err := s.readRemoteExcelData(ctx, fileUrl)
	if err != nil {
		stark.Logger.Errorf(ctx, "UserImport-readRemoteExcelData fail, err:%+v", err)
		return nil, err
	}
	// 数据校验&格式转换
	userList, errMsgList, cellErrFlag, err := s.excelDataToUserData(ctx, rowDatas)
	if err != nil {
		stark.Logger.Errorf(ctx, err.Error())
		return nil, err
	}

	resp := &base.UserImportResponse{}
	// 字段&格式错误标注
	if cellErrFlag && errMsgList != nil && len(errMsgList) > 0 {
		newFileurl, err := s.addImportUserDataFormatComment(ctx, errMsgList, fileUrl)
		if err != nil {
			stark.Logger.Errorf(ctx, "UserImport-addImportUserDataFormatComment fail, err:%+v", err)
			return nil, err
		}
		stark.Logger.Infof(ctx, "file url:%s", newFileurl)
		// response
		resp.FilePath = newFileurl
		resp.SuccessRowsNum = 0
		resp.FailRowsNum = int64(len(rowDatas) - 3)
		return resp, nil
	}

	// 写入错误信息&上传Oss
	importMsgList, err := s.userNewRepo.UserImport(ctx, userList)
	if err != nil {
		stark.Logger.Errorf(ctx, "导入用户信息失败：%v", err)
		return nil, err
	}
	if importMsgList != nil && len(importMsgList) > 0 {
		commentFileUrl, successNum, failNum, err := s.addImportUserDataErrorComment(ctx, importMsgList, fileUrl)
		if err != nil {
			stark.Logger.Errorf(ctx, "UserImport-addImportUserDataErrorComment fail, err:%+v", err)
			return nil, err
		}
		resp.FilePath = commentFileUrl
		resp.SuccessRowsNum = successNum
		resp.FailRowsNum = failNum
		return resp, nil
	}

	return nil, errors.New("导入失败")
}

// UserInfo 获取用户信息
func (s *userNewService) UserInfo(ctx context.Context, userId int64) (*base.UserInfoResponse, error) {
	params := &basePb.UserInfoRequest{
		UserId: userId,
	}
	info, err := s.userNewRepo.UserInfo(ctx, params)
	if err != nil {
		stark.Logger.Errorf(ctx, "call user info grpc failed:%v", err)
		return nil, err
	}

	userInfo := base.UserInfoItem{
		UserId:        info.UserId,
		UserGuid:      info.UserGuid,
		Account:       info.Account,
		Name:          info.Name,
		Mobile:        info.Mobile,
		Email:         info.Email,
		Position:      info.Position,
		ResetPassword: info.ResetPassword,
	}
	tenantInfo := base.TenantInfoItem{
		TenantCode: "ycg",
		TenantName: "明源云采购",
	}
	return &base.UserInfoResponse{
		User:   userInfo,
		Tenant: tenantInfo,
	}, nil
}

// SetPassword 设置用户密码
func (s *userNewService) SetPassword(ctx context.Context, param *base.SetPasswordRequest) (*base.SetPasswordResponse, error) {
	userIdTemp := ctx.Value(constants.LoginUserId)
	var userId int64
	if val, ok := userIdTemp.(int64); ok {
		userId = val
	}
	params := &basePb.ResetPasswordRequest{
		UserId:      userId,
		NewPassword: param.NewPassword,
	}
	_, err := s.userNewRepo.SetPassword(ctx, params)
	if err != nil {
		stark.Logger.Errorf(ctx, "call reset password grpc failed:%v", err)
		return nil, err
	}

	return new(base.SetPasswordResponse), nil
}

// ResetPassword 重置用户密码
func (s *userNewService) ResetPassword(ctx context.Context, param *base.ResetPasswordParam) error {
	err := s.userNewRepo.ResetPassword(ctx, param)
	if err != nil {
		stark.Logger.Errorf(ctx, "call reset password grpc failed:%v", err)
		return err
	}

	return nil
}

// UserDetail 用户详情
func (s *userNewService) UserDetail(ctx context.Context, userId int64) (*base.User, error) {
	return s.userNewRepo.UserDetail(ctx, userId)
}

// SetDepartmentHeader 设置负责人/取消负责人
func (s *userNewService) SetDepartmentHeader(ctx context.Context, param *base.SetDepartmentHeaderParam) error {
	return s.userNewRepo.SetDepartmentHeader(ctx, param)
}

// GetAllUserList 获取全量用户
func (s *userNewService) GetAllUserList(ctx context.Context) (*base.GetAllUserResponse, error) {
	data, err := s.userNewRepo.GetAllUserList(ctx)
	if err != nil {
		return nil, err
	}
	resp := &base.GetAllUserResponse{
		Total: 0,
		List:  []*base.GetAllUserItem{},
	}
	if data != nil {
		resp.Total = data.Total
		for _, item := range data.List {
			resp.List = append(resp.List, &base.GetAllUserItem{
				Id:       item.Id,
				UserName: item.UserName,
				Uid:      item.Uid,
			})
		}

	}
	return resp, nil
}

func (s *userNewService) GetAllUserWithOrganization(ctx context.Context) (*base.OrganizationWithUserDto, error) {
	data, err := s.userNewRepo.GetAllUserWithOrganization(ctx)
	if err != nil {
		return nil, err
	}
	if data == nil {
		return nil, errors.New("获取用户列表失败")
	}
	return setSliceToZero(data.RootOrg), nil
}

func setSliceToZero(orgWithUser *basePb.OrganizationWithUser) *base.OrganizationWithUserDto {
	if orgWithUser == nil {
		return nil
	}
	orgWithUserDto := &base.OrganizationWithUserDto{
		OrgId:    orgWithUser.Id,
		OrgName:  orgWithUser.OrgName,
		Users:    []*base.UserMiniInfoDto{},
		ChildOrg: []*base.OrganizationWithUserDto{},
	}
	for _, user := range orgWithUser.Users {
		orgWithUserDto.Users = append(orgWithUserDto.Users, &base.UserMiniInfoDto{
			Id:     user.Id,
			Name:   user.Name,
			Mobile: user.Mobile,
		})
	}

	for _, childOrg := range orgWithUser.ChildOrg {
		orgWithUserDto.ChildOrg = append(orgWithUserDto.ChildOrg, setSliceToZero(childOrg))
	}
	return orgWithUserDto
}

func (s *userNewService) excelDataToUserData(_ context.Context, rows [][]string) ([]*base.User, [][]string, bool, error) {
	cellErrFlag := false

	if len(rows) == 0 {
		return nil, nil, cellErrFlag, errors.New("导入文件内容为空")
	}
	if len(rows) <= 3 {
		return nil, nil, cellErrFlag, errors.New("导入文件内容为空")
	}
	errMsgList := [][]string{}
	userList := make([]*base.User, 0)
	//模板列名
	templateCols := []string{"用户名称", "密码", "所属组织", "角色", "手机", "邮箱", "职位", "是否区域负责人"}
	//必填列名
	mustInputCols := []string{"用户名称", "密码", "所属组织", "角色", "手机"}
	maxColLen := len(templateCols)
	mustInputColLen := len(mustInputCols)

	for i, row := range rows {
		//导入数据从第四行开始
		if i >= 3 {
			// 导入数据开始
			userItem := &base.User{}

			//检查导入数据内容&格式
			rowMsg := make([]string, maxColLen)
			for k, val := range row {
				val = strings.TrimSpace(val)
				// 必填字段检查
				if k < mustInputColLen {
					msg := ""
					if val == "" {
						msg = "内容不能为空"
					}
					if k == 4 && utils.IsValidPhoneNumber(val) == false {
						if val == "" {
							msg = "内容不能为空"
						} else {
							msg = "手机号码格式错误"
						}
					}
					if msg != "" {
						cellErrFlag = true
					}
					rowMsg[k] = msg
				} else if k == 5 && val != "" && utils.IsValidEmail(val) == false {
					msg := "邮箱格式错误"
					rowMsg[k] = msg
				}

				switch k {
				case 0:
					userItem.UserName = val
				case 1:
					userItem.Passwd = val
				case 2:
					userItem.OrgFullName = val
				case 3:
					userItem.RoleName = val
				case 4:
					userItem.Mobile = val
				case 5:
					userItem.Email = val
				case 6:
					userItem.Position = val
				case 7:
					if val == "是" {
						userItem.DepartmentHeader = 1
					} else {
						userItem.DepartmentHeader = 0
					}
				}
			}
			errMsgList = append(errMsgList, rowMsg)
			userList = append(userList, userItem)
			// 防止数组越界
			if len(row) < mustInputColLen {
				startIndex := len(row)
				endIndex := mustInputColLen - 1 //最后一个字段非必填
				for j := startIndex; j < endIndex; j++ {
					rowMsg[j] = "内容不能为空"
					cellErrFlag = true
				}
			}
		} else {
			// 模板内容
			if i == 2 { //校验模板是否正确
				for k, template := range templateCols {
					val := strings.TrimSpace(row[k])
					if val != template {
						return nil, nil, cellErrFlag, errors.New("模板格式错误")
					}
				}
			}
		}
	}

	return userList, errMsgList, cellErrFlag, nil
}

func (s *userNewService) addImportUserDataErrorComment(ctx context.Context, msgList []*basePb.ImportUserMsg, fileDownloadUrl string) (string, int64, int64, error) {
	if len(msgList) == 0 {
		stark.Logger.Errorf(ctx, "用户导入返回信息为空")
		return "", 0, 0, errors.New("导入失败")
	}
	successNum := 0
	failNum := 0
	errMsgList := [][]string{}
	for _, msg := range msgList {
		if msg.IsSuccess {
			successNum += 1
		} else {
			failNum += 1
		}

		errMsg := []string{
			0: msg.UserName,
			1: msg.Passwd,
			2: msg.FullName,
			3: msg.Role,
			4: msg.Mobile,
			7: msg.DepartmentHeader,
		}
		errMsgList = append(errMsgList, errMsg)
	}
	if failNum > 0 {
		newFileUrl, err := s.addImportUserDataFormatComment(ctx, errMsgList, fileDownloadUrl)
		if err != nil {
			return "", int64(successNum), int64(failNum), err
		}
		return newFileUrl, int64(successNum), int64(failNum), nil
	}

	return "", int64(successNum), int64(failNum), nil
}

func (s *userNewService) addImportUserDataFormatComment(ctx context.Context, errMsgList [][]string, fileDownloadUrl string) (string, error) {
	resp, err := http.Get(fileDownloadUrl)
	if err != nil {
		stark.Logger.Errorf(ctx, "addImportUserDataFormatComment ossfile download 失败：%s", err)
		return "", err
	}

	f, err := excelize.OpenReader(resp.Body)

	defer func() {
		if err = f.Close(); err != nil {
			stark.Logger.Errorf(ctx, "addImportUserDataFormatComment excelize close 失败：%+v", err)
		}
		if err = resp.Body.Close(); err != nil {
			stark.Logger.Errorf(ctx, "addImportUserDataFormatComment ossfile download close 失败：%+v", err)
		}
	}()

	for i, errMsg := range errMsgList {
		index := i + 4
		for k, val := range errMsg {
			if val == "" {
				continue
			}
			columnName := utils.IntToLetter(k)
			cellName := columnName + strconv.Itoa(index)

			err = f.AddComment("用户导入模板", excelize.Comment{
				Cell: cellName,
				Paragraph: []excelize.RichTextRun{
					{Text: val},
				},
			})
			if err != nil {
				stark.Logger.Errorf(ctx, "addImportUserDataFormatComment AddComment 失败：%s", err)
				return "", err
			}
		}
	}

	var updatedContent bytes.Buffer
	if err := f.Write(&updatedContent); err != nil {
		stark.Logger.Errorf(ctx, "addImportUserDataFormatComment-write fail, err:%+v", err)
		return "", err
	}

	tenantCode := "ycg" // 租户号，根据实际情况赋值
	fileExt := ".xlsx"  // 文件扩展类型，根据实际情况赋值
	filePath, _, err := ossfile.PutOssV2(bytes.NewReader(updatedContent.Bytes()), constants.PathUserOrganization, tenantCode, fileExt, 1)
	if err != nil {
		stark.Logger.Errorf(ctx, "addImportUserDataFormatComment-PutOssV2 fail, err:%+v", err)
		return "", err
	}

	newFileUrl, err := ossfile.GetSignURL(filePath)
	if err != nil {
		stark.Logger.Errorf(ctx, "addImportUserDataFormatComment-GetSignURL fail, err:%+v", err)
		return "", err
	}

	return newFileUrl, nil
}

func (s *userNewService) readRemoteExcelData(ctx context.Context, fileDownloadUrl string) ([][]string, error) {
	var rowData [][]string
	if fileDownloadUrl == "" {
		return rowData, nil
	}

	resp, err := http.Get(fileDownloadUrl)
	if err != nil {
		stark.Logger.Errorf(ctx, "ossfile download 失败：%s", err)
		return rowData, err
	}

	f, err := excelize.OpenReader(resp.Body)
	if err != nil {
		stark.Logger.Errorf(ctx, "excelize open 失败：%s", err)
		return rowData, errors.New("模板文件格式错误")
	}
	defer func() {
		if err = f.Close(); err != nil {
			stark.Logger.Errorf(ctx, "excelize close 失败：%s", err)
		}
		if err = resp.Body.Close(); err != nil {
			stark.Logger.Errorf(ctx, "ossfile download close 失败：%s", err)
		}
	}()

	rowData, err = f.GetRows("用户导入模板")
	if err != nil {
		stark.Logger.Errorf(ctx, "excelize read rows 失败：%s", err)
		return rowData, err
	}

	return rowData, nil
}

func (s *userNewService) GetMyResponsibleAreaUserList(ctx context.Context, param *base.GetAllUserRequest) (*base.GetAllUserResponse, error) {
	request := basePb.GetMyResponsibleAreaUserListRequest{
		Page:     param.Page,
		PageSize: param.PageSize,
	}
	list, err := s.userNewRepo.GetMyResponsibleAreaUserList(ctx, &request)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetMyResponsibleAreaUserList 失败：%s", err)
		return nil, err
	}

	response := &base.GetAllUserResponse{
		Total: int32(list.Total),
		List:  make([]*base.GetAllUserItem, 0),
	}

	if len(list.List) > 0 {
		for _, item := range list.List {
			response.List = append(response.List, &base.GetAllUserItem{
				Id:       item.Id,
				UserName: item.UserName,
				Uid:      item.Uid,
			})

		}
	}

	return response, nil
}
